#include "StdAfx.h"
#include "SGImage.h"

SGImage::SGImage( const std::string filename )
{
	m_filename = filename;
	h_xsize = h_ysize = 0;
	h_pixmin = 0;
	h_pixmax = 255;
	ot_lengthtab = NULL;
	ot_starttab = NULL;
	pixels = NULL;
}

SGImage::~SGImage(void)
{
	if( ot_lengthtab )
		delete[] ot_lengthtab;
	if( ot_starttab )
		delete[] ot_starttab;
	if( pixels )
		delete[] pixels;

	if( !v_lines.empty() )
	{
		std::vector< SGImageScanline* >::iterator it;

		for( it = v_lines.begin(); it != v_lines.end(); it++ )
			delete (*it);
	}
}

void SGImage::readHeader( void )
{
	h_magic = getShort( );
	if( h_magic != 474 )
	{
		fclose( imageFile );
		exit( 474 );
	}

	h_storage = getByte();
	h_bpc = getByte();
	h_dimension = getShort();
	h_xsize = getShort();
	h_ysize = getShort();
	h_zsize = getShort();
	h_pixmin = getLong();
	h_pixmax = getLong();	

	for( int i = 0; i < 4; i++ )
		h_dummy = getByte();

	for( int i = 0; i < 80; i++ )
		h_imagename[i] = getByte();

	h_colormap = getLong();

	if ( ( h_colormap < 0 ) || ( h_colormap > 3 ) )
		exit(4);

	for( int i = 0; i < 404; i++ )
	   h_dummy2[i] = getByte();
}

void SGImage::readTables( void )
{
	ot_starttab = new unsigned long[h_ysize * h_zsize];
	ot_lengthtab = new unsigned long[h_ysize * h_zsize];

	fseek( imageFile, 512, SEEK_SET );
	readlongtab( h_ysize * h_zsize, ot_starttab );
	readlongtab( h_ysize * h_zsize, ot_lengthtab );
}

unsigned short SGImage::getShort( void ) {
	return ( getByte() << 8 ) | getByte();
}

long SGImage::getLong( void )
{
	return ( getByte() << 24 ) | ( getByte() << 16 ) | ( getByte() << 8 ) | getByte();
}

unsigned char SGImage::getByte( void )
{
	return getc( imageFile );
}

void SGImage::readlongtab( int n, unsigned long *tab )
{
	int i;
	for ( i = 0 ; i < n ; i++ )
		tab[i] = getLong();
}

int SGImage::RLElength( int r, int c )
{
	return ot_lengthtab[ r + c * h_ysize ];
}

int SGImage::RLEoffset( int r, int c )
{
	return ot_starttab[ r + c * h_ysize ];
}

int SGImage::readScanline( int r, int c, int which_color )
{
	int offset = RLEoffset( r, c );
	int length = RLElength( r, c );
	SGImageScanline *line;// = findEqualScanline( offset, length );

	//if( line == NULL )
	//{
		unsigned char *bytes;
		bytes = new unsigned char[length];
		fseek( imageFile, offset, SEEK_SET );

		line = new SGImageScanline( offset, length );
		readByteTab( length, bytes );
		line->setBytes( bytes );
		delete[] bytes;
	//}

	v_lines.push_back( line );

	return 0;
}

SGImageScanline* SGImage::findEqualScanline( const int start, const int length )
{
	SGImageScanline *line = NULL;
	std::vector< SGImageScanline* >::iterator it;

	for( it = v_lines.begin(); it != v_lines.end(); it++ )
	{
		if( (*it) != NULL )
		{
			if( (*it)->getOffset() == start && (*it)->getLength() == length )
			{
				line = new SGImageScanline( *it );
				break;
			}
		}
	}

	return line;
}

void SGImage::decompressScanline( const int y, const int channel )
{
	unsigned short pixel;
	unsigned short count;
	SGImageScanline *line;

	int i = 0;
	int si = 0;
	int ind = 0;

	line = v_lines.back();
	v_lines.pop_back();

	iptr = line->getBytes();
	while(1)
	{
		pixel = iptr[si];
		si++;
		if( !(count = ( int )(pixel & 0x7f)) )
		{
			break;
		}

		if( pixel & 0x80 )
		{
			while(count--)
			{
				ind = i + h_xsize * y;
					
				switch( channel )
				{
					case 0:
						pixels[ ind ].r = ( unsigned short int )iptr[si];
						break;
					case 1:
						pixels[ ind ].g = ( unsigned short int )iptr[si];
						break;
					case 2:
						pixels[ ind ].b = ( unsigned short int )iptr[si];
						break;
					case 3:
						pixels[ ind ].a = ( unsigned short int )iptr[si];
						break;
				}
				si++;
				i++;
			}
		}
		else
		{
			pixel = iptr[si];
			si++;
			while(count--) 
			{
				ind = i + h_xsize * y;
				switch( channel )
				{
					case 0:
						pixels[ ind ].r = ( unsigned short int )pixel;
						break;
					case 1:
						pixels[ ind ].g = ( unsigned short int )pixel;
						break;
					case 2:
						pixels[ ind ].b = ( unsigned short int )pixel;
						break;
					case 3:
						pixels[ ind ].a = ( unsigned short int )pixel;
						break;
				}
				i++;
			}
		}
	}

	delete line;
}

void SGImage::readByteTab( unsigned int n, unsigned char *tab )
{
	int i;
	for( i = 0; i < n; i++ )
		tab[i] = getByte( );
}

void SGImage::loadSGImage( Image *p_image )
{
	imageFile = fopen( m_filename.c_str(), "rb" );

	readHeader();
	p_image->setWidth( h_xsize );
	p_image->setHeight( h_ysize );

	pixels = new Pixel[ h_xsize * h_ysize ];
	
	// testa se a imagem foi comprimida(RLE)
	if(h_storage == 1) //RLE
	{
		readTables();

		for( int i = 0; i < h_ysize; i++ )
			for( int j = 0; j < h_zsize; j++ )
				readScanline( i, j, j );
		
		for( int i = 0; i < h_ysize; i++ )
			for( int j = 0; j < h_zsize; j++ )
				decompressScanline( i, j );

		for( int i = 0; i < h_xsize; i++ )
			for( int j = 0; j < h_ysize; j++ )
			{
				int rgb = ( pixels[i + h_xsize * j].a << 24 ) | ( pixels[i + h_xsize * j].r << 16 ) | ( pixels[i + h_xsize * j].g << 8 ) | pixels[i + h_xsize * j].b;
				p_image->setRGB( i, h_ysize - j - 1, rgb );
			}

		
	} else if(h_storage == 0) // Verbatim - sem RLE
	{
		for( int k = 0; k < h_zsize; k++ )
			for( int i = 0; i < h_ysize; i++ )
				for( int j = 0; j < h_xsize; j++ )
				{
					unsigned char byte = getByte();
					switch( k )
					{
						case 0:
							pixels[j + h_xsize * i].r = ( unsigned short int )byte;
						case 1:
							pixels[j + h_xsize * i].g = ( unsigned short int )byte;
						case 2:
							pixels[j + h_xsize * i].b = ( unsigned short int )byte;
						case 3:
							pixels[j + h_xsize * i].a = ( unsigned short int )byte;
					}
				}

		for( int i = 0; i < h_xsize; i++ )
			for( int j = 0; j < h_ysize; j++ )
			{
				int rgb = ( pixels[i + h_xsize * j].a << 24 ) | ( pixels[i + h_xsize * j].r << 16 ) | ( pixels[i + h_xsize * j].g << 8 ) | pixels[i + h_xsize * j].b;
				p_image->setRGB( i, j, rgb );
			}
	}
	
	fclose( imageFile );
}